﻿using System;
using System.Diagnostics;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Nature.Toolkit
{
    public interface IObjectPool<T>
    {
        ObjectPoolItem<T> Acquire();

        void Release(ObjectPoolItem<T> obj);
    }

    [DebuggerDisplay("Count: {Count}")]
    public abstract class ObjectPool<T> : IObjectPool<T>
    {
        protected abstract ObjectPoolItem<T> CreateNew();

        public static IObjectPool<T> CreateObjectPool(Func<T> constructor)
        {
            return new GenericObjectPool<T>(constructor);
        }

        public int Count { get { return m_items.Count; } }

        #region Private Fields
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private Stack<ObjectPoolItem<T>> m_items = new Stack<ObjectPoolItem<T>>(10);
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private ReaderWriterLockSlim m_slim = new ReaderWriterLockSlim(); 
        #endregion        

        #region IObjectPool<T> Members

        public ObjectPoolItem<T> Acquire()
        {
            m_slim.EnterWriteLock();
            try
            {
                ObjectPoolItem<T> item =
                    m_items.Count > 0
                    ? m_items.Pop()
                    : this.CreateNew();
                return item;
            }
            finally
            {
                m_slim.ExitWriteLock();
            }
        }

        public void Release(ObjectPoolItem<T> obj)
        {
            m_slim.EnterWriteLock();
            try
            {
                m_items.Push(obj);
            }
            finally
            {
                m_slim.ExitWriteLock();
            }
        }

        #endregion

    }

    public abstract class ObjectPoolItem<T> : IDisposable
    {
        public T Unwrap()
        {
            return m_item;
        }

        #region Private Fields
        T m_item;
        IObjectPool<T> m_pool; 
        #endregion

        #region Constructor
        protected ObjectPoolItem(IObjectPool<T> pool, T item)
        {
            m_item = item;
            m_pool = pool;
        } 
        #endregion

        #region Object Overloads
        public override string ToString()
        {
            return m_item.ToString();
        }

        public override bool Equals(object obj)
        {
            if (obj is T)
            {
                return m_item.Equals(obj);
            }
            else if (obj is ObjectPoolItem<T>)
            {
                return m_item.Equals(((ObjectPoolItem<T>)obj).m_item);
            }
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return m_item.GetHashCode();
        } 
        #endregion

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            m_pool.Release(this);
        }

        #endregion
    }



    class GenericObjectPool<T> : ObjectPool<T>
    {
        private Func<T> m_constructor;

        internal GenericObjectPool(Func<T> constructor)
        {
            m_constructor = constructor;
        }

        protected override ObjectPoolItem<T> CreateNew()
        {
            return new GeneicObjectPoolItem<T>(this, m_constructor());
        }
    }

    class GeneicObjectPoolItem<T> : ObjectPoolItem<T>
    {
        internal GeneicObjectPoolItem(GenericObjectPool<T> pool, T item) : base(pool, item) { }
    }


    [DebuggerDisplay("Number of pools: {Count}")]
    public class ObjectPoolContainer
    {
        #region Private Fields
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private Hashtable m_pools = new Hashtable(); 
        #endregion

        public void Register<T>(IObjectPool<T> pool)
        {
            m_pools[typeof(T)] = pool;
        }

        public void Register<T>(Func<T> itemConstructor)
        {
            m_pools[typeof(T)] = new GenericObjectPool<T>(itemConstructor);
        }

        public void Register<T>() where T : new()
        {
            m_pools[typeof(T)] = new GenericObjectPool<T>(()=> new T());
        }

        public int Count { get { return m_pools.Count; } }

        public ObjectPoolItem<T> Acquire<T>()
        {
            try
            {
                return ((IObjectPool<T>)m_pools[typeof(T)]).Acquire();
            }
            catch (NullReferenceException ex)
            {
                Debug.Fail(ex.ToString());
                throw new ObjectPoolNotRegisteredException(typeof(T));   
            }
        }


    }


    public class ObjectPoolNotRegisteredException : InvalidOperationException
    {
        public ObjectPoolNotRegisteredException(Type type)
            : base()
        {
            ItemType = type;
        }

        public Type ItemType { get; private set; }
    }


}
